package com.css.bjdt_report.validation;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mvel2.MVEL;
import org.nutz.dao.Sqls;

import com.css.bjdt_report.cell.entity.Cell;
import com.css.bjdt_report.cell.entity.VarParam;
import com.css.bjdt_report.cell.entity.Cell.CellType;
import com.css.bjdt_report.cell.entity.CellData;
import com.css.bjdt_report.cell.service.CellDataService;
import com.css.bjdt_report.executive.report.ReportCache;
import com.css.bjdt_report.executive.entity.Exp;
import com.css.bjdt_report.report.entity.RptTemplateValidation.ValidationType;
import com.css.bjdt_report.report.entity.RptTemplateUnitRela;
import com.css.bjdt_report.report.service.ReportTemplateValidationService;
import com.css.bjdt_report.util.ReportConstant;
import com.css.bjdt_report.validation.entity.ValidationInfo;
import com.css.bjdt_report.validation.service.ValidationInfoService;
/**
 * 报表数据校核
 * 
 * @author qiushi
 *
 */
public class ReportDataValidator implements Validator<RptTemplateUnitRela> {	
	private static ReportDataValidator instance;
	
	private CellDataService cellDataSvc;
	
	private ReportTemplateValidationService validationSvc;
	
	private ValidationInfoService validationInfoSvc;
	
	private ReportCache reportCache = ReportCache.getInstance();
	
	private List<ValidationInfo> validations = null;
	
	private static final Log logger = LogFactory.getLog(ReportDataValidator.class);
	
	private ReportDataValidator(){
	};
	
	public static synchronized ReportDataValidator getInstance(){
		if(instance == null){
			instance = new ReportDataValidator();
		}
		return instance;
	}

	@Override
	public void validate(List<RptTemplateUnitRela> templateUnitRelas) {
		if(templateUnitRelas != null && templateUnitRelas.size() > 0){
			validations = new ArrayList<ValidationInfo>();
			String annualId = templateUnitRelas.get(0).getAnnualId();
			String dzzId = templateUnitRelas.get(0).getUnitId();
			for (RptTemplateUnitRela templateUnitRela : templateUnitRelas) {				
				validateByTemplate(templateUnitRela);
				validationInfoSvc.deleteValidationInfo(annualId, dzzId, templateUnitRela.getTemplateId());
			}
			validationInfoSvc.batchInsertValidationInfo(annualId, validations);
		}
	}
	/**
	 * 
	 * @param exps 校核集
	 * @param datas 数据集
	 * @param templateUnitRela 年度、单位、报表关系
	 * @param cellId 指标id
	 * @param type 校核类型
	 */
	private void validateExp(List<Exp> exps, List<CellData> cellDatas, RptTemplateUnitRela templateUnitRela, String cellId, ValidationType type){
		for (Exp exp : exps) {
			//获取校核表达式
			String actualExp = exp.getCellExp();			
			//如果校核类型为表内单元格校核或表间单元格校核
			if(ValidationType.inner_cell.equals(type) || ValidationType.outer_cell.equals(type)){
				//按关系运算符(><=)拆分
				String[] array = actualExp.split(ReportConstant.OPERATOR_RELA);	
				//生成参加校核公式的指标id
				String[] cellIds = (String[]) ArrayUtils.addAll(array[0].split(ReportConstant.OPERATOR_ARITH), array[1].split(ReportConstant.OPERATOR_ARITH));
				//获取相关指标数据
				cellDatas = cellDataSvc.getAllCellDataByCellIds(templateUnitRela.getAnnualId(), templateUnitRela.getUnitId(), cellIds);
			}
			String validationState = "2";//未通过
			if(cellDatas != null && cellDatas.size() > 0){
				List<String> cids = new ArrayList<String>(); 
				for (CellData cellData : cellDatas) {
					StringBuilder key = new StringBuilder();
					//按行列坐标生成key
					if(ValidationType.inner_row.equals(type) || ValidationType.inner_col.equals(type)){
						key.append("[").append(cellData.getRelRowNum()).append(ReportConstant.SPLIT_OPERATOR).append(cellData.getRelColNum()).append("]");					
					}
					//按指标id生成key
					else if(ValidationType.inner_cell.equals(type) || ValidationType.outer_cell.equals(type)){
						key.append(cellData.getCellId());
					}else{
						//...
					}
					//如果指标值不为null并且校核表达式包含key,那么将其替换
					if(cellData.getCellValue() != null){
						if(actualExp.contains(key.toString())){
							actualExp = actualExp.replace(key.toString(), CellType.cellNull.getKey().equals(cellData.getCellType()) ? "0" :cellData.getCellValue());						
						}
					}else{
						cids.add(cellData.getCellId());
					}
				}				
				try{
					validationState = "true".equals(MVEL.eval(actualExp).toString()) ? "1" : "2";				
				}catch(Exception e){
					//表内单元格校核时,如果所在报表的(统计类、计算类、填报类)指标均没值,那么校核通过		
					//表间单元格校核时,如果参加校核的任一指标所在报表的(统计类、计算类、填报类)指标均没值,那么校核通过
					/*if(ValidationType.inner_cell.equals(type) || ValidationType.outer_cell.equals(type)){
						for (String cid : cids) {
							int count = cellDataSvc.countCellDataByTemplate(templateUnitRela.getAnnualId(), templateUnitRela.getUnitId(), cid);
							if(count == 0){
								validationState = "1";//通过
								break;
							}
						}
					}*/										
					if("2".equals(validationState)){
						logger.debug("error exp is "+actualExp,e);
					}
				}
			}			
			ValidationInfo validationInfo = new ValidationInfo();
			validationInfo.setBatchId(templateUnitRela.getId());
			validationInfo.setSourceExp(exp.getFormulaShow());
			validationInfo.setCellExp(exp.getCellExp());
			validationInfo.setActualExp(actualExp);
			validationInfo.setValidationType(exp.getFormulaType());
			validationInfo.setValidationState(validationState);
			validationInfo.setCellId(cellId);
			validationInfo.setDzzId(templateUnitRela.getUnitId());
			validationInfo.setTemplateId(templateUnitRela.getTemplateId());
			validations.add(validationInfo);
		}
	}
	/**
	 * 按报表校核指标数据
	 * @param templateUnitRela
	 */
	private void validateByTemplate(RptTemplateUnitRela templateUnitRela){
		List<CellData> cellDatas = cellDataSvc.getCellData(templateUnitRela.getAnnualId(), templateUnitRela.getUnitId(), templateUnitRela.getTemplateId());		
		validateTableInnerRowExpAndColExp(cellDatas, templateUnitRela);
		validateTableInnerCellExp(cellDatas, templateUnitRela);
		validateTableOuterCellExp(templateUnitRela);
	}
	/**
	 * 表内行列校核
	 * @param cellDatas
	 * @param templateUnitRela
	 */
	private void validateTableInnerRowExpAndColExp(List<CellData> cellDatas, RptTemplateUnitRela templateUnitRela){
		Map<String,List<Cell>> cellMap = reportCache.getContent(templateUnitRela.getTemplateId());
		if(cellMap != null){
			List<Cell> cells = cellMap.get(CellType.vali.getKey());
			for (Cell cell : cells) {
				validateExp(cell.getValidationExps(), cellDatas, templateUnitRela, cell.getCellId(), ValidationType.inner_row);
			}			
		}
	}
	/**
	 * 表内单元格校核
	 * @param cellDatas
	 * @param templateUnitRela
	 */
	private void validateTableInnerCellExp(List<CellData> cellDatas, RptTemplateUnitRela templateUnitRela){
		List<Exp> validationExps = validationSvc.getValidationExpByType(templateUnitRela.getTemplateId(), ValidationType.inner_cell);
		validateExp(validationExps, cellDatas, templateUnitRela, null, ValidationType.inner_cell);
	}
	/**
	 * 表间单元格校核
	 * @param templateUnitRela
	 */
	private void validateTableOuterCellExp(RptTemplateUnitRela templateUnitRela){
		List<Exp> validationExps = validationSvc.getValidationExpByType(templateUnitRela.getTemplateId(), ValidationType.outer_cell);
		validateExp(validationExps, null, templateUnitRela, null, ValidationType.outer_cell);
	}
	
	public void isExistTable(String annualId){
		String tableName = ReportConstant.VALIDATION_TABLE_PREFIX+annualId;
		String count = validationInfoSvc.executeSql(ReportConstant.ISEXIST_TABLE_SQL, Sqls.callback.str(), new VarParam(null,null,"table_name",tableName)).getString();
		if("0".equals(count) || null == count){
			validationInfoSvc.executeSql(ReportConstant.VALIDATION_CREATE_TABLE_SQL, null, new VarParam("table_name",tableName,null,null));
		}
	}

	public void setCellDataSvc(CellDataService cellDataSvc) {
		this.cellDataSvc = cellDataSvc;
	}

	public void setValidationInfoSvc(ValidationInfoService validationInfoSvc) {
		this.validationInfoSvc = validationInfoSvc;
	}
	
	public void setValidationSvc(ReportTemplateValidationService validationSvc) {
		this.validationSvc = validationSvc;
	}

	public void destory(){
		if(validations != null){
			validations.clear();
			validations = null;
		}
	}
}
